package com.droid.lib.controls.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

import com.droid.lib.controls.R;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

/**
 * 说明：中国地图
 * Created by 李明杰 on 2017/3/24.
 */
public class ChinaMapView1 extends View {
    private int[] mstrColorArray = new int[]{R.color.color_svg_map_bg1, R.color.color_svg_map_bg2, R.color.color_svg_map_bg3, R.color.color_svg_map_bg4};
    private int mintSvgMap;
    private int mintMapColor1;
    private int mintMapColor2;
    private int mintMapColor3;
    private int mintMapColor4;
    private int mintDateNames;
    private String[] mstrDateNames;
    private int mintMaxTop;
    private int mintMaxBottom;
    private static final int DEFAULT_COLOR = Color.rgb(0xff, 0xff, 0xff);
    private static final int DEFAULT_SELECTD_COLOR = Color.rgb(0x00, 0xff, 0xff);
    private static final String TAG = "ChinaMapView";
    private int commonTextSixe = 30;
    private int textColor = Color.WHITE;
    private PointF[] mPointFs = new PointF[4];
    private float height = 0;
    private float width = 0;
    private int padding = 8;
    private float svgPathScale = 2.5f;
    private Paint xPaintsBorder;
    private Paint touchPaint;
    private int selected = -1;
    private Matrix xMatrix = new Matrix();
    private float translateX, translateY;
    private int viewHeight, viewWidth;
    private float minScale = 1;

    private float maxScale = 6;
    private float scale;
    private float defaultScale = 1;
    private int selectdColor = -1;
    private int mapColor = -1;
    private long startOnTouchTime = 0;
    GestureDetector gestureDetector;
    private List<AreaAttribute> mlistAreas;
    private AttributeSet attrs;


//    public enum Area {
//        BeiJing("北京市", 0),
//        TianJin("天津市", 1),
//        ShangHai("上海市", 2),
//        ChongQing("重庆市", 3),
//        HeBei("河北省", 4),
//        ShanXi("山西省", 5),
//        LiaoNing("辽宁省", 6),
//        HeiLongJiang("黑龙江省", 7),
//        JiLin("吉林省", 8),
//        JiangSu("江苏省", 9),
//        ZheJiang("浙江省", 10),
//        AnHui("安徽省", 11),
//        FuJian("福建省", 12),
//        JiangXi("江西省", 13),
//        ShanDong("山东省", 14),
//        HeNan("河南省", 15),
//        HuBei("湖北省", 16),
//        HuNan("湖南省", 17),
//        GuangDong("广东省", 18),
//        HaiNan("海南省", 19),
//        SiChuan("四川省", 20),
//        GuiZhou("贵州省", 21),
//        YunNan("云南省", 22),
//        ShaanXi("陕西省", 23),
//        GanSu("甘肃省", 24),
//        QingHai("青海省", 25),
//        NeiMengGu("内蒙古自治区", 26),
//        GuangXi("广西自治区", 27),
//        XiZang("西藏自治区", 28),
//        NingXia("宁夏自治区", 29),
//        XinJiang("新疆自治区", 30),
//        AoMen("澳门", 31),
//        XiangGang("香港", 32),
//        TaiWan("台湾", 33);
//        public int value;
//        public String name;
//
//        Area(String pName, int pValue) {
//            this.name = pName;
//            this.value = pValue;
//        }
//
//        public static Area valueOf(int value) {    //    手写的从int到enum的转换函数
//            switch (value) {
//                case 0:
//                    return BeiJing;
//                case 1:
//                    return TianJin;
//                case 2:
//                    return ShangHai;
//                case 3:
//                    return ChongQing;
//                case 4:
//                    return HeBei;
//                case 5:
//                    return ShanXi;
//                case 6:
//                    return LiaoNing;
//                case 7:
//                    return HeiLongJiang;
//                case 8:
//                    return JiLin;
//                case 9:
//                    return JiangSu;
//                case 10:
//                    return ZheJiang;
//                case 11:
//                    return AnHui;
//                case 12:
//                    return FuJian;
//                case 13:
//                    return JiangXi;
//                case 14:
//                    return ShanDong;
//                case 15:
//                    return HeNan;
//                case 16:
//                    return HuBei;
//                case 17:
//                    return HuNan;
//                case 18:
//                    return GuangDong;
//                case 19:
//                    return HaiNan;
//                case 20:
//                    return SiChuan;
//                case 21:
//                    return GuiZhou;
//                case 22:
//                    return YunNan;
//                case 23:
//                    return ShaanXi;
//                case 24:
//                    return GanSu;
//                case 25:
//                    return QingHai;
//                case 26:
//                    return NeiMengGu;
//                case 27:
//                    return GuangXi;
//                case 28:
//                    return XiZang;
//                case 29:
//                    return NingXia;
//                case 30:
//                    return XinJiang;
//                case 31:
//                    return AoMen;
//                case 32:
//                    return XiangGang;
//                case 33:
//                    return TaiWan;
//                default:
//                    return null;
//            }
//        }
//
//    }


    public void setPaintColor(AreaAttribute area, int color, boolean isFull) {
        Paint p = area.getSvgPaint();
        p.setColor(color);
        if (isFull) {
            p.setStyle(Paint.Style.FILL);
        }
        invalidate();
    }

    public void setPaintColor(int index, int color, boolean isFull) {
        Paint p = mlistAreas.get(index).getSvgPaint();
        p.setColor(color);
        if (isFull) {
            p.setStyle(Paint.Style.FILL);
        }
        invalidate();
    }

    public void setSelectdColor(int pSelectdColor) {
        this.selectdColor = pSelectdColor;
        invalidate();
    }

    public void setMapColor(int pMapColor) {
        mapColor = pMapColor;
        invalidate();
    }

    public void selectAProvince(AreaAttribute area) {
        if (selected == area.getNom()) {
            return;
        }
        selected = area.getNom();
        if (this.onProvinceSelectedListener != null)
            this.onProvinceSelectedListener.onProvinceSelected(area, false);
        invalidate();
    }

    public void selectProvince(String provinceName) {
        try {
            AreaAttribute area = null;
            for (int i = 0; i < mlistAreas.size(); i++) {
                if (mlistAreas.get(i).getName().equals(provinceName)) {
                    area = mlistAreas.get(i);
                    break;
                }
            }

            if (selected == area.getNom()) {
                return;
            }

            selected = area.getNom();
            if (this.onProvinceSelectedListener != null)
                this.onProvinceSelectedListener.onProvinceSelected(area, false);
            invalidate();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void initData() {
        final TypedArray attributes = getContext().obtainStyledAttributes(attrs, R.styleable.ChinaMapView);
        mintSvgMap = attributes.getResourceId(R.styleable.ChinaMapView_mapAddress, R.raw.china_svg);
        mintDateNames = attributes.getResourceId(R.styleable.ChinaMapView_mapNames, R.array.china_map);
        mintMapColor1 = attributes.getResourceId(R.styleable.ChinaMapView_mapColor1, R.color.color_svg_map_bg1);
        mintMapColor2 = attributes.getResourceId(R.styleable.ChinaMapView_mapColor2, R.color.color_svg_map_bg2);
        mintMapColor3 = attributes.getResourceId(R.styleable.ChinaMapView_mapColor3, R.color.color_svg_map_bg3);
        mintMapColor4 = attributes.getResourceId(R.styleable.ChinaMapView_mapColor4, R.color.color_svg_map_bg4);
        mintMaxTop = attributes.getInt(R.styleable.ChinaMapView_maxTop, 7);
        mintMaxBottom = attributes.getInteger(R.styleable.ChinaMapView_maxBottom, 19);
        mstrColorArray = new int[]{mintMapColor1, mintMapColor2, mintMapColor3, mintMapColor4};
        mlistAreas = new ArrayList<>();
        InputStream inputStream = getContext().getResources().openRawResource(mintSvgMap);
        mstrDateNames = getContext().getResources().getStringArray(mintDateNames);
        try {
            //取得 DocumentBuilderFactory 实例
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            //从 factory 获取 DocumentBuilder 实例
            DocumentBuilder builder = factory.newDocumentBuilder();
            //解析输入流,得到 Document 实例
            Document doc = builder.parse(inputStream);
            Element rootElement = doc.getDocumentElement();
            NodeList items = rootElement.getElementsByTagName("path");
            for (int i = 0; i < items.getLength(); i++) {
                Element element = (Element) items.item(i);
                String pathData = element.getAttribute("android:pathData");
                Path path = PathParser.createPathFromPathData(pathData);
                mlistAreas.add(new AreaAttribute(i, mstrDateNames[i], path));
                mlistAreas.get(i).setStrPath(pathData);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public ChinaMapView1(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.attrs = attrs;
        initData();
        initPaths();
        computeBounds();
        initPaints();
        gestureDetector = new GestureDetector(context, new mapGestureListener());
    }


    public List<AreaAttribute> getMlistAreas() {
        return mlistAreas;
    }

    public void setMlistAreas(List<AreaAttribute> mlistAreas) {
        this.mlistAreas = mlistAreas;
    }

    /**
     * 调整清晰度，竖直越大，地图越清晰，一般2.5倍即可，改成其他值之后，需要在drawOneArea方法中调整文字的大小和位置
     */


    private void initPaths() {
        try {
            SvgPathToAndroidPath lParser = new SvgPathToAndroidPath();
            lParser.setScale(svgPathScale);
            for (int i = 0; i < mlistAreas.size(); i++) {
                mlistAreas.get(i).setSvgPath(lParser.parser(mlistAreas.get(i).getStrPath()));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initPaints() {
        for (int i = 0; i < mlistAreas.size(); i++) {
            Paint xPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            xPaint.setColor(DEFAULT_COLOR);
            xPaint.setStyle(Paint.Style.FILL);
            mlistAreas.get(i).setSvgPaint(xPaint);
        }
        touchPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        touchPaint.setStyle(Paint.Style.FILL);
        touchPaint.setColor(DEFAULT_SELECTD_COLOR);

        xPaintsBorder = new Paint(Paint.ANTI_ALIAS_FLAG);
        xPaintsBorder.setStyle(Paint.Style.STROKE);
        xPaintsBorder.setColor(0xffc7a796);
        xPaintsBorder.setStrokeWidth(1.5f);
    }

    /**
     * 计算地图边界
     * 1.黑龙江是中国最东，最北的省份
     * 2.新疆是中国最西的省份
     * 3.海南是中国最南的省份
     * <p/>
     * 地图边界为
     * 0点                  1点
     * 0,0------------------heilongjiang.right,0
     * |                      |
     * |                      |
     * 0,hainan.bottom------heilongjiang.right,hainan.bottom
     * 3点                   2点
     * 地图宽度--->heilongjiang.right
     * 地图高度--->hainan.bottom
     */
    private void computeBounds() {

        RectF hljRF = new RectF();
        mlistAreas.get(mintMaxTop).getSvgPath().computeBounds(hljRF, true);
        RectF hnRF = new RectF();
        mlistAreas.get(mintMaxBottom).getSvgPath().computeBounds(hnRF, true);
        mPointFs[0] = new PointF(0, 0);
        mPointFs[1] = new PointF(hljRF.right, 0);
        mPointFs[2] = new PointF(hljRF.right, hnRF.bottom);
        mPointFs[3] = new PointF(0, hnRF.bottom);

        width = hljRF.right + 2 * padding;
        height = hnRF.bottom + 2 * padding;
        Log.e(TAG, "computeBounds: " + width + "  " + height);


    }

    private class mapGestureListener extends GestureDetector.SimpleOnGestureListener {


        @Override
        public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
            //延迟点击，在确定不是单击事件后才执行这个方法，效果不是很好
//            if ( Math.abs(motionEvent.getX() - startPoint.x) < 5f && Math.abs(motionEvent.getY() - startPoint.y) < 5f) {
//                try {
//                    for (int i = 0; i < xPaths.length; i++) {
//                        RectF r = new RectF();
//                        xPaths[i].computeBounds(r, true);
//                        MapRegion re = new MapRegion();
//                        re.setPath(xPaths[i], new MapRegion((int) r.left, (int) r.top, (int) r.right, (int) r.bottom));
//                        if (re.contains((int) (motionEvent.getX() / scale - translateX - padding), (int) (motionEvent.getY() / scale - translateY - padding))) {
//                            boolean doubleClick = i == selected;
//
//                            selected = i;
//                            if (onProvinceSelectedListener != null)
//                                onProvinceSelectedListener.onProvinceSelected(Area.valueOf(selected), doubleClick);
//
//
//                            invalidate();
//                            return true;
//                        }
//                    }
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//            }
            return super.onSingleTapConfirmed(motionEvent);
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            if (onProvinceDoubleClickListener != null) {
                onProvinceDoubleClickListener.onProvinceDoubleClick();
                return true;
            }
            return super.onDoubleTap(e);
        }
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int speSize = MeasureSpec.getSize(widthMeasureSpec);
        minScale = defaultScale = scale = speSize / width;
        Log.i(TAG, "onMeasure: " + speSize + "   " + minScale + "  " + (int) (speSize * height / width));
        setMeasuredDimension(speSize, (int) (speSize * height / width));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        scale = scale > maxScale ? maxScale : scale < minScale ? minScale : scale;
        xMatrix.setScale(scale, scale);
        canvas.concat(xMatrix);
        canvas.translate(translateX + padding, translateY + padding);
        drawBaseMap(canvas);
        drawSelectedMap(canvas);
    }


    private void drawBaseMap(Canvas pCanvas) {
        Paint textPaint = new Paint();

        for (int i = 0; i < mlistAreas.size(); i++) {
            textPaint.setTextSize(commonTextSixe);
            textPaint.setColor(textColor);

            if (mapColor != -1 && mlistAreas.get(i).getSvgPaint().getColor() == DEFAULT_COLOR) {
                mlistAreas.get(i).getSvgPaint().setColor(mapColor);
            }

            if (mlistAreas.get(i).getSvgPaint().getColor() != DEFAULT_COLOR) {
                textPaint.setColor(Color.parseColor("#860D0D"));
            }

            pCanvas.drawPath(mlistAreas.get(i).getSvgPath(), mlistAreas.get(i).getSvgPaint());
            pCanvas.drawPath(mlistAreas.get(i).getSvgPath(), xPaintsBorder);
//            if (Area.XiangGang == Area.valueOf(i) || Area.AoMen == Area.valueOf(i)) {
//                continue;
//            }
            drawOneArea(pCanvas, textPaint, i);

        }
    }

    private void drawOneArea(Canvas pCanvas, Paint textPaint, int index) {
        RectF testRect = new RectF();
        mlistAreas.get(index).getSvgPath().computeBounds(testRect, true);
        int paddingLeft = 0;
        int paddingTop = 0;
        mlistAreas.get(index).setPaddingTop(paddingTop);
        mlistAreas.get(index).setPaddingLeft(paddingLeft);
        switch (index) {
            case 26:
                mlistAreas.get(26).setPaddingTop(paddingTop += 180);
                mlistAreas.get(26).setPaddingLeft(paddingLeft -= 120);
                break;
            case 30:
                mlistAreas.get(30).setPaddingTop(paddingTop += 50);
                mlistAreas.get(30).setPaddingLeft(paddingLeft -= 100);
                break;
            case 28:
                mlistAreas.get(28).setPaddingLeft(paddingLeft -= 110);
                break;
            case 24:
                mlistAreas.get(24).setPaddingTop(paddingTop += 90);
                mlistAreas.get(24).setPaddingLeft(paddingLeft += 50);
                break;
            case 2:
                mlistAreas.get(2).setPaddingLeft(paddingLeft += 7);
                break;
            case 18:
                mlistAreas.get(18).setPaddingTop(paddingTop -= 20);
                break;
            case 23:
                mlistAreas.get(23).setPaddingTop(paddingTop += 38);
                break;
            case 5:
                mlistAreas.get(5).setPaddingLeft(paddingLeft -= 20);
                break;
            case 7:
                mlistAreas.get(7).setPaddingLeft(paddingLeft -= 25);
                mlistAreas.get(7).setPaddingTop(paddingTop += 30);
                break;
            case 4:
                mlistAreas.get(4).setPaddingLeft(paddingLeft -= 50);
                mlistAreas.get(4).setPaddingTop(paddingTop += 50);
                break;
            case 0:
                mlistAreas.get(0).setPaddingLeft(paddingLeft += 0);
                mlistAreas.get(0).setPaddingTop(paddingTop += 1);
                textPaint.setTextSize(14);
                break;
            case 1:
                mlistAreas.get(1).setPaddingLeft(paddingLeft -= 3);
                mlistAreas.get(1).setPaddingTop(paddingTop += 10);
                textPaint.setTextSize(14);
                break;
            case 29:
                mlistAreas.get(29).setPaddingLeft(paddingLeft -= 15);
                mlistAreas.get(29).setPaddingTop(paddingTop += 10);
                textPaint.setTextSize(21);
                break;
            case 3:
                mlistAreas.get(3).setPaddingLeft(paddingLeft -= 25);
                mlistAreas.get(3).setPaddingTop(paddingTop += 25);
                textPaint.setTextSize(22);
                break;
            case 13:
                mlistAreas.get(13).setPaddingLeft(paddingLeft -= 25);
                break;
            case 11:
                mlistAreas.get(11).setPaddingLeft(paddingLeft -= 25);
                break;
            case 22:
                mlistAreas.get(22).setPaddingLeft(paddingLeft -= 25);
                break;
        }

        pCanvas.drawText(mstrDateNames[index], testRect.left + testRect.width() / 2 - padding + paddingLeft, testRect.top + testRect.height() / 2 + paddingTop, textPaint);
    }

    private void drawSelectedMap(Canvas pCanvas) {
        if (selected >= 0) {
            if (selectdColor != -1) {
                touchPaint.setColor(selectdColor);
            }
            pCanvas.drawPath(mlistAreas.get(selected).getSvgPath(), touchPaint);
            Paint textPaint = new Paint();
            textPaint.setStyle(Paint.Style.FILL);
            textPaint.setColor(0xffffffff);
            textPaint.setTextSize(commonTextSixe);
            drawOneArea(pCanvas, textPaint, selected);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        viewWidth = w;
        viewHeight = h;
    }


    @Override
    public boolean onTouchEvent(MotionEvent pMotionEvent) {
        gestureDetector.onTouchEvent(pMotionEvent);

        switch (pMotionEvent.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                mode = NONE;
                if (pMotionEvent.getPointerCount() == 1) {
                    startOnTouchTime = System.currentTimeMillis();
                    mode = NONE;
                    startPoint.set(pMotionEvent.getX(), pMotionEvent.getY());

                }
                break;
            case MotionEvent.ACTION_UP:
                mode = NONE;
                if (pMotionEvent.getPointerCount() == 1) {
                    long timeCount = System.currentTimeMillis() - startOnTouchTime;
                    if (timeCount < 4000 && Math.abs(pMotionEvent.getX() - startPoint.x) < 5f && Math.abs(pMotionEvent.getY() - startPoint.y) < 5f) {
                        try {
                            for (int i = 0; i < mlistAreas.size(); i++) {
                                RectF r = new RectF();
                                mlistAreas.get(i).getSvgPath().computeBounds(r, true);
                                Region re = new Region();
                                re.setPath(mlistAreas.get(i).getSvgPath(), new Region((int) r.left, (int) r.top, (int) r.right, (int) r.bottom));
                                if (re.contains((int) (pMotionEvent.getX() / scale - translateX - padding), (int) (pMotionEvent.getY() / scale - translateY - padding))) {
                                    boolean doubleClick = i == selected;
                                    selected = i;
                                    if (this.onProvinceSelectedListener != null)
                                        this.onProvinceSelectedListener.onProvinceSelected(mlistAreas.get(selected), doubleClick);


                                    invalidate();
                                    return true;
                                }
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                break;
            default:
                break;
        }
        return true;
    }

    /**
     * 模式 NONE：无. MOVE：移动. ZOOM:缩放
     */

    private static final int NONE = 0;
    private static final int MOVE = 1;
    private static final int ZOOM = 2;
    private int mode = NONE;    // 默认模式

    private double startDis = 0d;
    private PointF startPoint = new PointF();

    /**
     * 多触点
     *
     * @param event
     */
    private void onPointerDown(MotionEvent event) {
        if (event.getPointerCount() == 2) {
            mode = ZOOM;
            startDis = getDistance(event);
        }
    }


    private void onTouchMove(MotionEvent event) {
        if (mode == ZOOM && event.getPointerCount() == 2) {
            double endDis = getDistance(event);//结束距离
            if (endDis > 10f) {
                float tmpScale = (float) (endDis / startDis);//放大倍数
                if (tmpScale == 1) {
                    return;
                } else {
                    scale = tmpScale;
                    invalidate();
                }
            }
        } else {
            long timeCount = System.currentTimeMillis() - startOnTouchTime;
            if (timeCount > 300 && Math.abs(event.getX() - startPoint.x) > 10f && Math.abs(event.getY() - startPoint.y) > 10f) {
                translateX = event.getX() - startPoint.x;
                translateY = event.getY() - startPoint.y;
                invalidate();
            }
        }
    }

    /**
     * @param event
     * @return 获取两手指之间的距离
     */
    private double getDistance(MotionEvent event) {
        double x = event.getX(0) - event.getX(1);
        double y = event.getY(0) - event.getY(1);
        return Math.sqrt(x * x + y * y);
    }

    /**
     * 计算两点之间中心点的距离
     *
     * @param event
     * @return
     */
    private static PointF mid(MotionEvent event) {
        float midx = event.getX(1) + event.getX(0);
        float midy = event.getY(1) - event.getY(0);

        return new PointF(midx / 2, midy / 2);
    }

    public interface OnProvinceSelectedListener {
        void onProvinceSelected(AreaAttribute areaAttribute, boolean repeatClick);
    }

    public interface OnProvinceDoubleClickListener {
        void onProvinceDoubleClick();
    }

    private OnProvinceSelectedListener onProvinceSelectedListener;
    private OnProvinceDoubleClickListener onProvinceDoubleClickListener;

    public void setOnProvinceSelectedListener(OnProvinceSelectedListener pOnProvinceSelectedListener) {
        this.onProvinceSelectedListener = pOnProvinceSelectedListener;
    }

    public void setOnProvinceDoubleClickListener(OnProvinceDoubleClickListener onDoubleClickListener) {
        this.onProvinceDoubleClickListener = onDoubleClickListener;
    }

}
